----------Spanish for Mastery----------
A 4am and san inc crack      2022-07-02
---------------------------------------

Name: Spanish for Mastery
Genre: educational
Year: 1985
Credits: Jean-Paul Valette,
  Rebecca M. Valette
Publisher: D.C. Heath
Platform: Apple ][+ or later (64K)
Media: 5.25-inch disk
Sides: 6
OS: Diversi-DOS C1983
Previous cracks: none

This disk was automatically cracked by
Passport. Here is the transcript for
disk 1A:

                 --v--

Reading from S6,D1
T00,S00 Found Diversi-DOS bootloader
Using disk's own RWTS
Writing to RAM disk
T03 Found nibble count protection track
T02,S02 Volume name is C1983 DSR C#254
T00,S05 Found a self-decrypting
protection check at $BB03.
T00,S05,$03: 4E06BB716E0ABB4027 -> A9B54
8A919484C93B7
Writing to S6,D2
Crack complete. Press any key

                 --^--

More information and source code is
available at
https://archive.org/details/Passport4am

Other sides are unprotected.

Quod erat liberand one more thing...

                   ~

Every DOS 3.3-formatted Apple II floppy
has a "disk volume number." It is set
when you format the disk, is stored in
the address field of every sector, and
is displayed when you issue the CATALOG
command. The disk volume number is
almost always ignoreable, except when
it isn't.

In this program, it isn't.

The program disk has the default disk
volume number (254), but the data disks
are sequentially numbered:

  - disk 1A: 254
  - disk 1B: 101
  - disk 2A: 102
  - disk 2B: 103
  - disk 3A: 104
  - disk 3B: 105

And they matter. The program uses the
disk volume number to know whether the
right disk is in the drive at any
particular time.

Why is this a problem? In the physical
world, it isn't. Unprotected disks can
have any disk volume number and still
be copyable with third-party tools. But
it's the 21st century, and we don't
traffic in physical objects; we traffic
in disk images.

Due to poor historical choices, the
dominant format for disk images, .DSK,
does not store the disk volume number.
This presents us with a modern dilemma
of our own design: either release disk
images in some other format (easy, but
could reduce compatibility or require
more disk space or both), or change the
game code to distinguish disks in some
other way (hard, game-specific, insane,
why would you do that).

Obviously, we chose the hard way.

To distinguish disks that are really
.DSK files, we need to store a unique
marker somewhere on the disk that will
actually be stored in the .DSK file.
Since .DSK files only contain sector
data from track $00 to track $22, that
means we need to store a byte in a
sector. That part is non-negotiable.
Data must be stored somewhere, and if
.DSK files only store sector data, we
need to store the fake volume number as
sector data.

Here's what we're going to do. We're
going to store the disk volume number
in a sector that has real data,

AND intercept the game's low-level disk
reading routine so that instead of
getting the disk volume from the
address field, it gets it from that
magic byte in that magic sector,

AND intercept low-level format commands
to write the disk volume number to that
magic byte of that magic sector,

AND intercept low-level read commands
of that magic sector to return the real
sector data that belongs there instead
of the magic byte,

AND cache the magic byte from the magic
sector so we're not actually reading
the magic sector for every single disk
access (which would totally destroy the
performance of an already slow medium).

I said it was insane. You were warned.

                   ~

Our code begins at $BEB0. This region
is supposed to be part of the format
disk routine, but it is now unused for
reasons that will be explained shortly.

[Note: source code for this routine was
 provided along with this write-up. If
 you didn't get it, well, I don't know
 what to tell you.]

[Note the second: qkumba wrote this
 code. I mostly just cheered.]

*BEB0L

; Save address of RWTS parameter table
BEB0-   84 48       STY   $48
BEB2-   85 49       STA   $49

; Check the requested disk volume
BEB4-   A0 03       LDY   #$03
BEB6-   B1 48       LDA   ($48),Y

; 0 = wildcard = always continue
BEB8-   F0 4D       BEQ   $BF07
BEBA-   AA          TAX

; Store the requested disk volume
; because we'll need it later
BEBB-   8D 01 BF    STA   $BF01

; Fake out the real RWTS by putting a 0
; in the requested disk volume
BEBE-   A9 00       LDA   #$00
BEC0-   91 48       STA   ($48),Y

; X holds the requested disk volume --
; check if it's the same as the last
; RWTS command
BEC2-   EC 03 BF    CPX   $BF03

; Yes, they match, which means we can
; go ahead and execute the RWTS command
BEC5-   F0 40       BEQ   $BF07

; No, this RWTS command is requesting a
; different disk volume than the last
; RWTS command. It's time for our magic
; act!
BEC7-   20 33 BF    JSR   $BF33

Track $11, sector $01 is the best
choice to store our fake disk volume
byte, for one reason: it is unused on
all disks. (This is common on DOS 3.3.)
None of the disks are writeable, so we
won't have to intercept writes to this
sector, which simplifies the code
enormously.

*BF33L

; swap out the requested track/sector/
; command with
;   track = $11
;   sector = $01
;   command = $01 (read)
; and save the original values so we
; can restore them later
BF33-   A2 04       LDX   #$04
BF35-   BC 4A BF    LDY   $BF4A,X
BF38-   B1 48       LDA   ($48),Y
BF3A-   48          PHA
BF3B-   BD 49 BF    LDA   $BF49,X
BF3E-   91 48       STA   ($48),Y
BF40-   68          PLA
BF41-   9D 49 BF    STA   $BF49,X
BF44-   CA          DEX
BF45-   CA          DEX
BF46-   10 ED       BPL   $BF35
BF48-   60          RTS
BF49-   01 0C 01 05 11 04

Continuing from $BECA...

; After swapping the RWTS parameters,
; the accumulator has the original RWTS
; command. Check if it's trying to
; format a disk.
; Now, I don't THINK this title ever
; tries to format anything -- it does
; not, for instance, support user data
; disks -- but just to be safe, we're
; going to assume the disk is already
; formatted, and write the disk volume
; as regular data in our magic sector.
; (This code is mostly a holdover from
; a previous title where we used this
; same trick.)
; First, convert $04 (format command)
; to $02 (write command).
BECA-   C9 04       CMP   #$04
BECC-   D0 14       BNE   $BEE2
BECE-   4A          LSR

; Get address from RWTS parameter table
BECF-   91 48       STA   ($48),Y
BED1-   A0 08       LDY   #$08
BED3-   B1 48       LDA   ($48),Y
BED5-   85 3E       STA   $3E
BED7-   C8          INY
BED8-   B1 48       LDA   ($48),Y
BEDA-   85 3F       STA   $3F

; Set the first byte in the buffer to
; 254
BEDC-   A0 00       LDY   #$00
BEDE-   A9 FE       LDA   #$FE
BEE0-   91 3E       STA   ($3E),Y

; Execution continues here regardless
; (possibly from the branch at $BECC,
; or by falling through after setting
; up the fake format command)
BEE2-   20 30 BF    JSR   $BF30

*BF30L

; call the real RWTS
BF30-   20 04 BD    JSR   $BD04

After the real RWTS returns, $BF30 will
fall through to $BF33, which is the
routine we called earlier to swap the
track/sector/command in the RWTS
parameter table. So now they've swapped
back to their original values.

Continuing from $BEE5...

; Restore read command
BEE5-   A9 01       LDA   #$01
BEE7-   8D 49 BF    STA   $BF49

; If the real RWTS came back with an
; error, cancel all the magic and
; propagate the error back to the
; caller
BEEA-   B0 35       BCS   $BF21

; Real RWTS is OK, so on with the show!
; Y = #$0C when the real RWTS returns,
; which is handy because we want to get
; the RWTS command from the parameter
; table.
BEEC-   B1 48       LDA   ($48),Y
BEEE-   AA          TAX

; I just want to point out that this
; address spells "BEEF", which is funny
; but irrelevant to the task at hand.
; "Where's the $BEEF?" "There it is!"
; Never mind, this entire comment was a
; misteak.
BEEF-   C8          INY

; Set the error in the RWTS parameter
; table to "disk volume mismatch."
; (This will only be checked by the
; caller if we return with the carry
; bit set, which we haven't decided
; yet, so it's safe to do this now.)
BEF0-   A9 20       LDA   #$20
BEF2-   91 48       STA   ($48),Y

; Get the fake disk volume from the
; first byte of the sector data
BEF4-   A0 00       LDY   #$00
BEF6-   B1 3E       LDA   ($3E),Y

; Save it for next time
BEF8-   8D 03 BF    STA   $BF03

; If the original RWTS command was $04
; (format), we've done all the magic
; we're going to do today, so tell the
; caller that it worked and be happy.
BEFB-   E0 04       CPX   #$04
BEFD-   18          CLC
BEFE-   F0 21       BEQ   $BF21

; These next two instructions are self-
; modifying code. $BF01 is the disk
; volume that was originally requested
; (set at $BEBB), and $BF03 is the disk
; volume that we want the caller to
; believe is currently in the drive
; (set at $BEF8).
BF00-   A9 FE       LDA   #$FE
BF02-   C9 FE       CMP   #$FE

; If they don't match, set the carry to
; indicate an error (we already set the
; RWTS error code to "disk volume
; mismatch, at $BEF2), then exit via
; the cleanup routine at $BF21 which
; will complete the illusion by setting
; the requested and found disk volume
; in the RWTS parameter table.
BF04-   38          SEC
BF05-   D0 1A       BNE   $BF21

There are 3 possible ways we can end up
here.

  1) The caller requested a wildcard
     disk volume ($00), so we branched
     from $BEB8.

  2) The caller requested a disk volume
     that was the same as the previous
     RWTS call. We assumed the correct
     disk is already in the drive and
     branched here from $BEC5.

  3) The caller requested a disk volume
     that was different from the last
     RWTS call, we did our magic to get
     the fake volume from T11,S01 and
     discovered that the correct disk
     is now in the drive, so we fell
     through from $BF05.

; Execute the original RWTS command
BF07-   20 04 BD    JSR   $BD04

; Didn't work, propagate the error and
; exit
BF0A-   B0 15       BCS   $BF21

Hooray! The original RWTS command
succeeded! Just one last thing...

; Was this the game trying to read
; T11,S01?
BF0C-   A0 04       LDY   #$04
BF0E-   B1 48       LDA   ($48),Y
BF10-   C9 11       CMP   #$11
BF12-   D0 0C       BNE   $BF20
BF14-   C8          INY
BF15-   B1 48       LDA   ($48),Y
BF17-   C9 01       CMP   #$01
BF19-   D0 05       BNE   $BF20

; Yes, which means the first byte of
; the sector data we just read is the
; fake disk volume. Change that to the
; real data before returning to the
; caller. Presto-chango!
BF1B-   A9 00       LDA   #$00
BF1D-   A8          TAY
BF1E-   91 3E       STA   ($3E),Y

For this title, all disks have a #$00
as the first byte of the magic sector
(T11,S01), so swapping out the real
sector data is easy -- it's always 0!
But if that weren't the case, we could
keep a map of expected data indexed by
disk volume. That may be required if
we use this code for other titles.

This literary technique is called
4shadowing.

; Reset the carry to tell the caller
; that the RWTS command succeeded. (The
; CMPs to check the track and sector
; messed it up.)
BF20-   18          CLC

All code paths lead here. This is the
final cleanup that we always do before
returning to the caller: setting the
requested and found disk volumes in the
RWTS parameter table. This does not
affect the carry bit.

BF21-   A0 03       LDY   #$03
BF23-   AD 01 BF    LDA   $BF01
BF26-   91 48       STA   ($48),Y
BF28-   A0 0E       LDY   #$0E
BF2A-   AD 03 BF    LDA   $BF03
BF2D-   91 48       STA   ($48),Y
BF2F-   60          RTS

Now, each disk gets its own magic byte
on track $11, sector $01, byte $00 to
represent the volume for that disk:

disk 1A: T11,S01,$00: 00 -> FE
disk 1B: T11,S01,$00: 00 -> 65
disk 2A: T11,S01,$00: 00 -> 66
disk 2B: T11,S01,$00: 00 -> 67
disk 3A: T11,S01,$00: 00 -> 68
disk 3B: T11,S01,$00: 00 -> 69 (nice)

Finally, we need one single patch. All
RWTS calls go through $BD00. If we
change that to jump to $BEB0 instead,
the illusion will be complete.

T00,S07,$00: 84488549 -> EA4CB0BE

Quod erat liberandum.

---------------------------------------
A 4am and san inc crack        No. 2900
------------------EOF------------------
